//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <elasysapi.h>
#include <stdlib.h>
#include "crt0.h"

_ELASTOS_NAMESPACE_USING

static int ParseArgs(wchar_t *wargv[], wchar_t *wszName, wchar_t *wszArgs);

extern Int32 g_lNeedCleanUp;
EXTERN_C PATEXIT_DATA g_pDllExitStack;

#if !defined(_MSVC)
static _ELASTOS Boolean GetIsDebug()
{
    IProcess *pCurPro = CProcess::GetCurrent();
    IMemoryTrace *pMemoryTrace = NULL;
    Boolean debug = FALSE;
    pMemoryTrace = IMemoryTrace::Probe(pCurPro);
    if (!pMemoryTrace) {
        return FALSE;
    }
    pMemoryTrace->IsTraceDebug(&debug);
    return debug;
}

#define DEBUG_PROGRAM GetIsDebug()
#else
#define DEBUG_PROGRAM 0
#endif

#ifdef _GNUC
#define _CRTIMP  __attribute__ ((dllimport))
#else
#define _CRTIMP  __declspec(dllimport)
#endif

#ifdef _INTEL
// BUGBUG:Intel compiler bug.
//        Intel compiler force use c++ symbol for "wmain" function.
EXTERN_C int __cdecl _wmain(int argc, wchar_t* wargv[]);
#define wmain _wmain
#endif

EXTERN_C void mainCRTStartup(void);

ECode GetCurrModulePath(WStringBuf* pwsCurrentPath)
{
    IModule * pModule;
    ECode ec = CModule::GetCurrent(&pModule);
#ifdef _win32
    while (E_INVALID_OPERATION == ec) {
        ec = CModule::GetCurrent(&pModule);
    }
#endif
    if (FAILED(ec)) {
        return ec;
    }

    ec = pModule->GetFullPath(pwsCurrentPath);
    pModule->Release();
    if (FAILED(ec)) {
        return ec;
    }

    Int32 idx1 = pwsCurrentPath->LastIndexOf(L'/');
    Int32 idx2 = pwsCurrentPath->LastIndexOf(L'\\');
    Int32 idx = idx1 > idx2 ? idx1 : idx2;
    if (idx) pwsCurrentPath->SetLength(idx + 1);


    return NOERROR;
}

void mainCRTStartup(void)
{
    int argc;
    wchar_t *wargv[MAXIMUM_ARGC];
    WStringBuf_<MAXIMUM_FULL_PATH_LENGTH> esbCmdName;
    WStringBuf_<MAXIMUM_ARGS_LENGTH> esbCmdArgs;
    g_pDllExitStack = (PATEXIT_DATA)-1;
    _crtinit(1);
    __exeatexit(_dtorlist);
    _Impl_GetStartInfo(&esbCmdName, &esbCmdArgs);

    argc = ParseArgs(wargv, (wchar_t*)(esbCmdName.GetPayload()),
                    (wchar_t*)(esbCmdArgs.GetPayload()));

    if (DEBUG_PROGRAM) DebugBreak();
    BufferOf<WString> box((WString *)wargv, argc + 1, argc);
    PVoid pAppletObj;
    PInterface pCallbackContext;
    WStringBuf_<260> wsCurrentPath;

    ECode ec = _Impl_TApplet_Create(&pAppletObj, &pCallbackContext);
    if (FAILED(ec)) exit(ec);;

    _CThread_SetLocalObject(TL_CALLBACK_SLOT, pCallbackContext);
    _Impl_CallbackSink_SetDefaultCallbackContext(pCallbackContext);

    ec = _Impl_TApplet_Prepare(pAppletObj);
    if (FAILED(ec)) exit(ec);
    ec = GetCurrModulePath(&wsCurrentPath);
    if (SUCCEEDED(ec)) _Impl_TApplet_SetAppletPath(pAppletObj, &wsCurrentPath);
    ec = ElastosMain(box);

    ec = _Impl_TApplet_DoWork(pAppletObj, ec);

    exit(ec);
}

static int ParseArgs(wchar_t *wargv[], wchar_t *wszName, wchar_t *wszArgs)
{
    int argc;
    wchar_t *pCurPos;

    argc = 1; // at least one argument(the program name)
    wargv[0] = wszName;
    pCurPos = wszArgs;

    // TODO: handle back slash
    for (;;) {
        if (0 == *pCurPos) break;
//        SkipBlank(pCurPos);
        while (*pCurPos == ' ' || *pCurPos == '\t') pCurPos++;
        if (0 == *pCurPos) break;

        if ('\"' == *pCurPos) {
            wargv[argc++] = ++pCurPos; // skip double-quote
            while (('\"' != *pCurPos) && (0 != *pCurPos)) {
                pCurPos++;
            }
            if (0 == *pCurPos) break;
            *pCurPos = 0;  // end of one arg
            pCurPos++;
        }
        else {
            wargv[argc++] = pCurPos;
            while ((' ' != *pCurPos) && ('\t' != *pCurPos)
                && (0 != *pCurPos)) {
                pCurPos++;
            }
            if (0 == *pCurPos) break;
            *pCurPos = 0;  // end of one arg
            pCurPos++;
        }
        if (argc == MAXIMUM_ARGC - 1) break;
    }
    wargv[argc] = NULL;

    return argc;
}
